﻿/**
 *
 * at_command.c
 *
 *  Created on: 2019-11-26
 *      Author: chenshisheng
 */

#include "at_command.h"
#include "at_client.h"
#include "stream/mem_stream.h"
#include "message_queue.h"

#include <stdarg.h>
#include <stdlib.h>
#include <ctype.h>

typedef int (*_EventHandler)(char *respValue);

typedef struct
{
    const char *name;
    _EventHandler handler;
}_EventCmd_t;

static int _OnMiplEvent(char *respValue);
static int _OnMiplWrite(char *respValue);
static int _OnMiplDiscover(char *respValue);

static const _EventCmd_t _cmds[] =
{
        {"MIPLEVENT", _OnMiplEvent},
        {"MIPLWRITE", _OnMiplWrite},
        {"MIPLDISCOVER", _OnMiplDiscover},
};

static const char *_dataTypeStrings[] =
{
        "String",
        "Opaque",
        "Integer",
        "Float",
        "Boolean",
        "HexString",
};

static uint16_t _notifyAckId;
static AtCommand_WriteHandler _writeHandler;

void AtCommand_Init(AtCommand_WriteHandler writer)
{
    _notifyAckId = 1;
    _writeHandler = writer;

    AtClient_Init();
}

int AtCommand_At(void)
{
    return AtClient_Send(100, "AT");
}

int AtCommand_Reboot(void)
{
    AtClient_Send(0, "AT+NRB");

    return 0;
}

int AtCommand_CSQ(void)
{
    int ret;
    char *argv[2];
    int argc;

    ret = AtClient_Send(1000, "AT+CSQ");
    if(ret != 0)
    {
        return -1;
    }

    argc = AtClient_RespValueList(argv, ARRAY_SIZE(argv));
    if(argc < 2)
    {
        return -2;
    }

    return atoi(argv[0]);
}

/**
 * @brief 设置模组在网络连接状态变化后是否上报消息
 * @param n 大于0时，模组将上报连接状态消息
 * @return 0 成功，否则失败
 */
int AtCommand_CSCON(int n)
{
    return  AtClient_Send(1000, "AT+CSCON=%d", n);
}

AtCommand_NetworkRegState_t AtCommand_ReadNetworkRegState(void)
{
    AtCommand_NetworkRegState_t state;
    int ret;
    char *argv[2];
    int argc;

    state = AtCommand_NetworkRegState_Unknown;
    ret = AtClient_Send(1000, "AT+CEREG?");
    if(ret != 0)
    {
        return state;
    }

    argc = AtClient_RespValueList(argv, ARRAY_SIZE(argv));
    if(argc < 2)
    {
        return state;
    }

    ret = atoi(argv[1]);
    state = (AtCommand_NetworkRegState_t)ret;
    return state;
}

int AtCommand_MiplCreate(void)
{
    return AtClient_Send(30 * 1000, "AT+MIPLCREATE");
}

int AtCommand_MiplDelete(void)
{
    return AtClient_Send(3000, "AT+MIPLDELETE=0");
}

int AtCommand_MiplAutoUpdate(bool isEnable)
{
    return AtClient_Send(3000, "AT+MIPLUPDATESET=0,%c", isEnable ? '1' : '0');
}

int AtCommand_MiplAddObj(int objId, int insCount)
{
    char bitMap[9];
    int i;

    if(insCount > 8)
    {
        return -1;
    }

    for(i = 0; i < insCount; i++)
    {
        bitMap[i] = '1';
    }

    bitMap[i] = '\0';
    return AtClient_Send(1000, "AT+MIPLADDOBJ=0,%d,%d,\"%s\",0,0", objId, insCount, bitMap);
}

int AtCommand_MiplDelObj(int objId)
{
    return AtClient_Send(1000, "AT+MIPLDELOBJ=0,%d", objId);
}

int AtCommand_MiplDiscoverRsp(int objId, const int *resIds, unsigned int resCound)
{
    char data[128];
    MemStream_t ms;
    int i;

    if(resCound < 1)
    {
        return -1;
    }

    MemStream_Init(&ms, data, sizeof(data) - 1);
    chprintf(&ms, "%d", resIds[0]);
    for(i = 1; i < resCound; i++)
    {
        chprintf(&ms, ";%d", resIds[i]);
    }

    if(! Stream_Put(& ms, '\0'))
    {
        data[sizeof(data) - 1] = '\0';
    }

    return AtClient_Send(1000, "AT+MIPLDISCOVERRSP=0,%d,1,%d,\"%s\"", objId, strlen(data), data);
}

int AtCommand_MiplOpen(int lifetime, int timeout)
{
    return AtClient_Send(3000, "AT+MIPLOPEN=0,%d,%d", lifetime, timeout);
}

int AtCommand_MiplClose(void)
{
    return AtClient_Send(3000, "AT+MIPLCLOSE=0");
}

int AtCommand_MiplUpdate(int lifetime)
{
    return AtClient_Send(3000, "AT+MIPLUPDATE=0,%d,0", lifetime);
}

int AtCommand_MiplNotify(
        int objId,
        int insId,
        int resId,
        AtCommand_DataType_t type,
        AtCommand_Value_t value)
{
    char buf[32];
    const char *valueStr;

    switch(type)
    {
        case AtCommand_DataType_String:
        case AtCommand_DataType_Opaque:
        case AtCommand_DataType_HexString:
            valueStr = value.sv;
            break;

        case AtCommand_DataType_Integer:
            snprintf(buf, sizeof(buf), "%d", value.iv);
            valueStr = buf;
            break;

        case AtCommand_DataType_Float:
            snprintf(buf, sizeof(buf), "%.4f", value.fv);
            valueStr = buf;
            break;

        case AtCommand_DataType_Bool:
            snprintf(buf, sizeof(buf), "%02d", value.bv ? 1 : 0);
            valueStr = buf;
            break;

        default:
            return -1;
    }

    if(_notifyAckId == 0)
    {
        _notifyAckId = 1;
    }

    return AtClient_Send(3000, "AT+MIPLNOTIFY=0,0,%d,%d,%d,%d,%d,\"%s\",0,0",
            objId, insId, resId, type, strlen(valueStr), valueStr);
}

int AtCommand_MiplWriteRsp(int mid, AtCommand_WriteResult_t result)
{
    return AtClient_Send(3000, "AT+MIPLWRITERSP=0,%d,%d", mid, (int)result);
}

char * AtCommand_ReadIMEI(void)
{
    if(AtClient_Send(1000, "AT+CGSN=1") == 0)
    {
        return AtClient_RespValue();
    }
    else
    {
        return "";
    }
}

char * AtCommand_ReadIMSI(void)
{
    if(AtClient_Send(2000, "AT+CIMI") == 0)
    {
        return AtClient_RespValue();
    }
    else
    {
        return "";
    }
}

void AtCommand_OnEvent(const MsgQueue_Msg_t *msg)
{
    char buf[AT_CLIENT_RX_BUF_SIZE + 1];
    char *p, *str, *name, *value;
    unsigned int i;

    memcpy(buf, msg->data, msg->size);
    buf[msg->size] = '\0';
    str = buf;

    // 去除行首空白
    for(i = 0; i < msg->size; i++)
    {
        if(isspace(*str))
        {
            str ++;
        }
        else
        {
            break;
        }
    }

    debug("AtRx: %s", str);

    // 处理AT事件
    p = strchr(str, '+');
    if(p == NULL)
    {
        return;
    }

    name = p + 1;
    p = strchr(name, ':');
    if(p == NULL)
    {
        return ;
    }

    *p = '\0';
    value = p + 1;
    for(i = 0; i < ARRAY_SIZE(_cmds); i++)
    {
        if(strcmp(name, _cmds[i].name) == 0)
        {
            _cmds[i].handler(value);
        }
    }
}

static int _OnMiplEvent(char *respValue)
{
    int n;
    char *argv[3];
    AtCommand_MiplEvent_t event;

    n = AtClient_Split(respValue, ',', argv, ARRAY_SIZE(argv));
    if(n < 2)
    {
        debug("%s: Not Match!\n", __FUNCTION__);
        return -1;
    }

    event = atoi(argv[1]);
    MsgQueue_Send(MsgQueue_Id_MiplEvent, &event, sizeof(event));

    return 0;
}

static int _OnMiplWrite(char *respValue)
{
    int n;
    char *argv[8];
    AtCommand_WriteInfo_t info;
    AtCommand_WriteResult_t result;

    n = AtClient_Split(respValue, ',', argv, ARRAY_SIZE(argv));
    if(n < 8)
    {
        debug("%s: Not Match!\n", __FUNCTION__);
        ON_RELEASE(printf("ATCommand: write request error!\n");)
        return -1;
    }

    info.mid   = atoi(argv[1]);
    info.objId = atoi(argv[2]);
    info.insId = atoi(argv[3]);
    info.resId = atoi(argv[4]);
    info.type  = atoi(argv[5]);
    info.len   = atoi(argv[6]);
    info.valueString = argv[7];
    ON_RELEASE(printf("ATCommand: write request: %d_%d_%d: %s\n",
            info.objId, info.insId, info.resId, info.valueString);)

    result = _writeHandler(&info);
    return AtCommand_MiplWriteRsp(info.mid, result);
}

static int _OnMiplDiscover(char *respValue)
{
    int argc;
    char *argv[3];
    int objId;

    argc = AtClient_Split(respValue, ',', argv, ARRAY_SIZE(argv));
    if(argc < ARRAY_SIZE(argv))
    {
        debug("%s: Not Match!\n", __FUNCTION__);
        return -1;
    }

    objId = atoi(argv[2]);
    MsgQueue_Send(MsgQueue_Id_MiplDiscoverObj, &objId, sizeof(objId));
    return 0;
}

const char *AtCommand_DateTypeString(AtCommand_DataType_t type)
{
    return _dataTypeStrings[type - AtCommand_DateType_Min];
}

int AtCommand_ReadIdCmd(Stream_t *stream, int argc, char *argv[])
{
    const char *str;

    str = AtCommand_ReadIMEI();
    chprintf(stream, "IMEI: %s\n", str);

//    HAL_Delay(10);
    str = AtCommand_ReadIMSI();
    chprintf(stream, "IMSI: %s\n", str);

    return 0;
}

int AtCommand_ShellCmd(Stream_t *stream, int argc, char *argv[])
{
    int respondTimeoutMs = 100;
    int ret;

    if(argc < 2)
    {
        chprintf(stream, "TX: %s\n", argv[0]);
        ret = AtClient_Send(respondTimeoutMs, "%s", argv[0]);
    }
    else
    {
        if(argc >= 3)
        {
            respondTimeoutMs = atoi(argv[2]);
            if(respondTimeoutMs < 100)
            {
                respondTimeoutMs = 100;
            }
        }

        chprintf(stream, "TX: %s+%s\n", argv[0], argv[1]);
        ret = AtClient_Send(respondTimeoutMs, "%s+%s", argv[0], argv[1]);
    }

    if(ret == 0)
    {
        chprintf(stream, "RX: %s", AtClient_RespValue());
    }
    else
    {
        chprintf(stream, "Error: %d\n", ret);
    }

    return 0;
}
